Proper connect_port
[juce-lv2.git] / juce / source / extras / the jucer / src / model / jucer_ComponentLayout.cpp
blobea094e0c811af2abec155c9f84c4f142d631ec07
1 /*
2 ==============================================================================
4 This file is part of the JUCE library - "Jules' Utility Class Extensions"
5 Copyright 2004-11 by Raw Material Software Ltd.
7 ------------------------------------------------------------------------------
9 JUCE can be redistributed and/or modified under the terms of the GNU General
10 Public License (Version 2), as published by the Free Software Foundation.
11 A copy of the license is included in the JUCE distribution, or can be found
12 online at www.gnu.org/licenses.
14 JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 A PARTICULAR PURPOSE. See the GNU General Public License for more details.
18 ------------------------------------------------------------------------------
20 To release a closed-source product which uses JUCE, commercial licenses are
21 available: visit www.rawmaterialsoftware.com/juce for more information.
23 ==============================================================================
26 #include "../jucer_Headers.h"
27 #include "jucer_JucerDocument.h"
28 #include "jucer_ObjectTypes.h"
29 #include "../ui/jucer_JucerDocumentHolder.h"
30 #include "components/jucer_ComponentUndoableAction.h"
33 //==============================================================================
34 ComponentLayout::ComponentLayout()
35 : document (0),
36 nextCompUID (1)
40 ComponentLayout::~ComponentLayout()
42 components.clear();
45 //==============================================================================
46 void ComponentLayout::changed()
48 if (document != 0)
49 document->changed();
52 void ComponentLayout::perform (UndoableAction* action, const String& actionName)
54 jassert (document != 0);
56 if (document != 0)
58 document->getUndoManager().perform (action, actionName);
60 else
62 action->perform();
63 delete action;
67 //==============================================================================
68 void ComponentLayout::clearComponents()
70 selected.deselectAll();
71 components.clear();
72 changed();
75 //==============================================================================
76 class AddCompAction : public UndoableAction
78 public:
79 AddCompAction (XmlElement* const xml_, ComponentLayout& layout_)
80 : indexAdded (-1),
81 xml (xml_),
82 layout (layout_)
86 ~AddCompAction()
88 delete xml;
91 bool perform()
93 showCorrectTab();
94 Component* const newComp = layout.addComponentFromXml (*xml, false);
95 jassert (newComp != 0);
97 indexAdded = layout.indexOfComponent (newComp);
98 jassert (indexAdded >= 0);
99 return indexAdded >= 0;
102 bool undo()
104 showCorrectTab();
105 layout.removeComponent (layout.getComponent (indexAdded), false);
106 return true;
109 int getSizeInUnits() { return 10; }
111 int indexAdded;
113 private:
114 XmlElement* xml;
115 ComponentLayout& layout;
117 static void showCorrectTab()
119 JucerDocumentHolder* const docHolder = JucerDocumentHolder::getActiveDocumentHolder();
121 if (docHolder != 0)
122 docHolder->showLayout();
125 AddCompAction (const AddCompAction&);
126 AddCompAction& operator= (const AddCompAction&);
129 //==============================================================================
130 class DeleteCompAction : public ComponentUndoableAction <Component>
132 public:
133 DeleteCompAction (Component* const comp, ComponentLayout& layout)
134 : ComponentUndoableAction <Component> (comp, layout),
135 oldIndex (-1)
137 ComponentTypeHandler* const h = ComponentTypeHandler::getHandlerFor (*comp);
138 jassert (h != 0);
139 if (h != 0)
140 xml = h->createXmlFor (comp, &layout);
141 else
142 xml = 0;
144 oldIndex = layout.indexOfComponent (comp);
147 ~DeleteCompAction()
149 delete xml;
152 bool perform()
154 showCorrectTab();
155 layout.removeComponent (getComponent(), false);
156 return true;
159 bool undo()
161 Component* c = layout.addComponentFromXml (*xml, false);
162 jassert (c != 0);
164 layout.moveComponentZOrder (layout.indexOfComponent (c), oldIndex);
166 showCorrectTab();
167 return c != 0;
170 private:
171 XmlElement* xml;
172 int oldIndex;
175 void ComponentLayout::removeComponent (Component* comp, const bool undoable)
177 if (comp != 0 && components.contains (comp))
179 if (undoable)
181 perform (new DeleteCompAction (comp, *this), "Delete components");
183 else
185 selected.deselect (comp);
186 selected.changed (true); // synchronous message to get rid of any property components
188 components.removeObject (comp);
189 changed();
194 //==============================================================================
195 class FrontBackCompAction : public ComponentUndoableAction <Component>
197 public:
198 FrontBackCompAction (Component* const comp, ComponentLayout& layout, int newIndex_)
199 : ComponentUndoableAction <Component> (comp, layout),
200 newIndex (newIndex_)
202 oldIndex = layout.indexOfComponent (comp);
205 bool perform()
207 showCorrectTab();
208 Component* comp = layout.getComponent (oldIndex);
209 layout.moveComponentZOrder (oldIndex, newIndex);
210 newIndex = layout.indexOfComponent (comp);
211 return true;
214 bool undo()
216 showCorrectTab();
217 layout.moveComponentZOrder (newIndex, oldIndex);
218 return true;
221 private:
222 int newIndex, oldIndex;
225 void ComponentLayout::moveComponentZOrder (int oldIndex, int newIndex)
227 jassert (components [oldIndex] != 0);
229 if (oldIndex != newIndex && components [oldIndex] != 0)
231 components.move (oldIndex, newIndex);
232 changed();
236 void ComponentLayout::componentToFront (Component* comp, const bool undoable)
238 if (comp != 0 && components.contains (comp))
240 if (undoable)
241 perform (new FrontBackCompAction (comp, *this, -1), "Move components to front");
242 else
243 moveComponentZOrder (components.indexOf (comp), -1);
247 void ComponentLayout::componentToBack (Component* comp, const bool undoable)
249 if (comp != 0 && components.contains (comp))
251 if (undoable)
252 perform (new FrontBackCompAction (comp, *this, 0), "Move components to back");
253 else
254 moveComponentZOrder (components.indexOf (comp), 0);
259 //==============================================================================
260 const char* const ComponentLayout::clipboardXmlTag = "COMPONENTS";
262 void ComponentLayout::copySelectedToClipboard()
264 if (selected.getNumSelected() == 0)
265 return;
267 XmlElement clip (clipboardXmlTag);
269 for (int i = 0; i < components.size(); ++i)
271 Component* const c = components.getUnchecked(i);
273 if (selected.isSelected (c))
275 ComponentTypeHandler* const type = ComponentTypeHandler::getHandlerFor (*c);
277 jassert (type != 0);
278 if (type != 0)
280 XmlElement* const e = type->createXmlFor (c, this);
281 clip.addChildElement (e);
286 SystemClipboard::copyTextToClipboard (clip.createDocument (String::empty, false, false));
289 void ComponentLayout::paste()
291 XmlDocument clip (SystemClipboard::getTextFromClipboard());
292 XmlElement* const doc = clip.getDocumentElement();
294 if (doc != 0 && doc->hasTagName (clipboardXmlTag))
296 selected.deselectAll();
298 forEachXmlChildElement (*doc, e)
300 Component* newComp = addComponentFromXml (*e, true);
302 if (newComp != 0)
303 selected.addToSelection (newComp);
306 startDragging();
307 dragSelectedComps (Random::getSystemRandom().nextInt (40),
308 Random::getSystemRandom().nextInt (40));
309 endDragging();
312 delete doc;
315 void ComponentLayout::deleteSelected()
317 const SelectedItemSet <Component*> temp (selected);
318 selected.deselectAll();
319 selected.changed (true); // synchronous message to get rid of any property components
321 if (temp.getNumSelected() > 0)
323 for (int i = temp.getNumSelected(); --i >= 0;)
324 removeComponent (temp.getSelectedItem (i), true);
326 changed();
328 if (document != 0)
329 document->dispatchPendingMessages(); // forces the change to propagate before a paint() callback can happen,
330 // in case there are components floating around that are now dangling pointers
334 void ComponentLayout::selectAll()
336 for (int i = 0; i < components.size(); ++i)
337 selected.addToSelection (components.getUnchecked (i));
340 void ComponentLayout::selectedToFront()
342 const SelectedItemSet <Component*> temp (selected);
344 for (int i = temp.getNumSelected(); --i >= 0;)
345 componentToFront (temp.getSelectedItem(i), true);
348 void ComponentLayout::selectedToBack()
350 const SelectedItemSet <Component*> temp (selected);
352 for (int i = 0; i < temp.getNumSelected(); ++i)
353 componentToBack (temp.getSelectedItem(i), true);
356 void ComponentLayout::bringLostItemsBackOnScreen (int width, int height)
358 for (int i = components.size(); --i >= 0;)
360 Component* const c = components[i];
362 if (! c->getBounds().intersects (Rectangle<int> (0, 0, width, height)))
364 c->setTopLeftPosition (width / 2, height / 2);
365 updateStoredComponentPosition (c, false);
370 Component* ComponentLayout::addNewComponent (ComponentTypeHandler* const type, int x, int y)
372 Component* c = type->createNewComponent (getDocument());
373 jassert (c != 0);
375 if (c != 0)
377 c->setSize (type->getDefaultWidth(), type->getDefaultHeight());
378 c->setCentrePosition (x, y);
379 updateStoredComponentPosition (c, false);
381 c->getProperties().set ("id", nextCompUID++);
383 XmlElement* xml = type->createXmlFor (c, this);
384 delete c;
386 c = addComponentFromXml (*xml, true);
387 delete xml;
389 String memberName (makeValidCppIdentifier (type->getClassName (c), true, true, false));
390 setComponentMemberVariableName (c, memberName);
392 selected.selectOnly (c);
395 return c;
399 Component* ComponentLayout::addComponentFromXml (const XmlElement& xml, const bool undoable)
401 if (undoable)
403 AddCompAction* const action = new AddCompAction (new XmlElement (xml), *this);
404 perform (action, "Add new components");
406 return components [action->indexAdded];
408 else
410 ComponentTypeHandler* const type
411 = ComponentTypeHandler::getHandlerForXmlTag (xml.getTagName());
413 if (type != 0)
415 Component* const newComp = type->createNewComponent (getDocument());
417 if (type->restoreFromXml (xml, newComp, this))
419 // ensure that the new comp's name is unique
420 setComponentMemberVariableName (newComp, getComponentMemberVariableName (newComp));
422 // check for duped IDs..
423 while (findComponentWithId (ComponentTypeHandler::getComponentId (newComp)) != 0)
424 ComponentTypeHandler::setComponentId (newComp, Random::getSystemRandom().nextInt64());
426 components.add (newComp);
427 changed();
428 return newComp;
430 else
432 delete newComp;
436 return 0;
440 Component* ComponentLayout::findComponentWithId (const int64 componentId) const
442 for (int i = 0; i < components.size(); ++i)
443 if (ComponentTypeHandler::getComponentId (components.getUnchecked(i)) == componentId)
444 return components.getUnchecked(i);
446 return 0;
449 //==============================================================================
450 static const char* const dimensionSuffixes[] = { "X", "Y", "W", "H" };
452 Component* ComponentLayout::getComponentRelativePosTarget (Component* comp, int whichDimension) const
454 jassert (comp != 0);
456 PaintElement* const pe = dynamic_cast <PaintElement*> (comp);
458 if (pe != 0)
460 int64 compId;
462 if (whichDimension == 0)
463 compId = pe->getPosition().relativeToX;
464 else if (whichDimension == 1)
465 compId = pe->getPosition().relativeToY;
466 else if (whichDimension == 2)
467 compId = pe->getPosition().relativeToW;
468 else
469 compId = pe->getPosition().relativeToH;
471 return findComponentWithId (compId);
473 else
475 return findComponentWithId (comp->getProperties() [String ("relativeTo") + dimensionSuffixes [whichDimension]]
476 .toString().getHexValue64());
480 void ComponentLayout::setComponentRelativeTarget (Component* comp, int whichDimension, Component* compToBeRelativeTo)
482 PaintElement* const pe = dynamic_cast <PaintElement*> (comp);
484 jassert (comp != 0);
485 jassert (pe != 0 || components.contains (comp));
486 jassert (compToBeRelativeTo == 0 || components.contains (compToBeRelativeTo));
487 jassert (compToBeRelativeTo == 0 || ! dependsOnComponentForRelativePos (compToBeRelativeTo, comp));
489 if (compToBeRelativeTo != getComponentRelativePosTarget (comp, whichDimension)
490 && (compToBeRelativeTo == 0 || ! dependsOnComponentForRelativePos (compToBeRelativeTo, comp)))
492 const int64 compId = ComponentTypeHandler::getComponentId (compToBeRelativeTo);
494 Rectangle<int> oldBounds (comp->getBounds());
495 RelativePositionedRectangle pos;
497 if (pe != 0)
499 oldBounds = pe->getCurrentBounds (dynamic_cast <PaintRoutineEditor*> (pe->getParentComponent())->getComponentArea());
500 pos = pe->getPosition();
502 else
504 pos = ComponentTypeHandler::getComponentPosition (comp);
507 if (whichDimension == 0)
508 pos.relativeToX = compId;
509 else if (whichDimension == 1)
510 pos.relativeToY = compId;
511 else if (whichDimension == 2)
512 pos.relativeToW = compId;
513 else if (whichDimension == 3)
514 pos.relativeToH = compId;
516 if (pe != 0)
518 pe->setPosition (pos, true);
519 pe->setCurrentBounds (oldBounds, dynamic_cast <PaintRoutineEditor*> (pe->getParentComponent())->getComponentArea(), true);
521 else
523 setComponentPosition (comp, pos, true);
524 comp->setBounds (oldBounds);
525 updateStoredComponentPosition (comp, false);
528 changed();
532 bool ComponentLayout::dependsOnComponentForRelativePos (Component* comp, Component* possibleDependee) const
534 for (int i = 0; i < 4; ++i)
536 Component* const c = getComponentRelativePosTarget (comp, i);
537 if (c != 0 && (c == possibleDependee || dependsOnComponentForRelativePos (c, possibleDependee)))
538 return true;
541 return false;
544 const int menuIdBase = 0x63240000;
546 PopupMenu ComponentLayout::getRelativeTargetMenu (Component* comp, int whichDimension) const
548 PopupMenu m;
550 Component* const current = getComponentRelativePosTarget (comp, whichDimension);
552 m.addItem (menuIdBase, "Relative to parent component", true, current == 0);
553 m.addSeparator();
555 for (int i = 0; i < components.size(); ++i)
557 Component* const c = components.getUnchecked(i);
559 if (c != comp)
561 m.addItem (menuIdBase + i + 1,
562 "Relative to " + getComponentMemberVariableName (c)
563 + " (class: " + ComponentTypeHandler::getHandlerFor (*c)->getClassName (c) + ")",
564 ! dependsOnComponentForRelativePos (c, comp),
565 current == c);
569 return m;
572 void ComponentLayout::processRelativeTargetMenuResult (Component* comp, int whichDimension, int menuResultID)
574 if (menuResultID != 0)
576 Component* const newTarget = components [menuResultID - menuIdBase - 1];
577 setComponentRelativeTarget (comp, whichDimension, newTarget);
581 //==============================================================================
582 class ChangeCompPositionAction : public ComponentUndoableAction <Component>
584 public:
585 ChangeCompPositionAction (Component* const comp, ComponentLayout& layout,
586 const RelativePositionedRectangle& newPos_)
587 : ComponentUndoableAction <Component> (comp, layout),
588 newPos (newPos_)
590 oldPos = ComponentTypeHandler::getComponentPosition (comp);
593 bool perform()
595 showCorrectTab();
596 layout.setComponentPosition (getComponent(), newPos, false);
597 return true;
600 bool undo()
602 showCorrectTab();
603 layout.setComponentPosition (getComponent(), oldPos, false);
604 return true;
607 private:
608 RelativePositionedRectangle newPos, oldPos;
611 void ComponentLayout::setComponentPosition (Component* comp,
612 const RelativePositionedRectangle& newPos,
613 const bool undoable)
615 if (ComponentTypeHandler::getComponentPosition (comp) != newPos)
617 if (undoable)
619 perform (new ChangeCompPositionAction (comp, *this, newPos), "Move components");
621 else
623 ComponentTypeHandler::setComponentPosition (comp, newPos, this);
624 changed();
629 void ComponentLayout::updateStoredComponentPosition (Component* comp, const bool undoable)
631 RelativePositionedRectangle newPos (ComponentTypeHandler::getComponentPosition (comp));
633 newPos.updateFromComponent (*comp, this);
635 setComponentPosition (comp, newPos, undoable);
638 //==============================================================================
639 void ComponentLayout::startDragging()
641 for (int i = 0; i < components.size(); ++i)
643 Component* const c = components[i];
644 c->getProperties().set ("xDragStart", c->getX());
645 c->getProperties().set ("yDragStart", c->getY());
648 jassert (document != 0);
649 document->getUndoManager().beginNewTransaction();
652 void ComponentLayout::dragSelectedComps (int dx, int dy, const bool allowSnap)
654 if (allowSnap && document != 0 && selected.getNumSelected() > 1)
656 dx = document->snapPosition (dx);
657 dy = document->snapPosition (dy);
660 for (int i = 0; i < selected.getNumSelected(); ++i)
662 Component* const c = selected.getSelectedItem (i);
664 const int startX = c->getProperties() ["xDragStart"];
665 const int startY = c->getProperties() ["yDragStart"];
667 if (allowSnap && document != 0 && selected.getNumSelected() == 1)
669 c->setTopLeftPosition (document->snapPosition (startX + dx),
670 document->snapPosition (startY + dy));
672 else
674 c->setTopLeftPosition (startX + dx,
675 startY + dy);
678 updateStoredComponentPosition (c, false);
682 void ComponentLayout::endDragging()
684 // after the drag, roll back all the comps to their start position, then
685 // back to their finish positions using an undoable command.
686 document->getUndoManager().beginNewTransaction();
688 for (int i = 0; i < selected.getNumSelected(); ++i)
690 Component* const c = selected.getSelectedItem (i);
692 const int newX = c->getX();
693 const int newY = c->getY();
695 const int startX = c->getProperties() ["xDragStart"];
696 const int startY = c->getProperties() ["yDragStart"];
698 c->setTopLeftPosition (startX, startY);
699 updateStoredComponentPosition (c, false);
701 c->setTopLeftPosition (newX, newY);
702 updateStoredComponentPosition (c, true);
705 document->getUndoManager().beginNewTransaction();
708 void ComponentLayout::moveSelectedComps (int dx, int dy, bool snap)
710 startDragging();
711 dragSelectedComps (dx, dy, snap);
712 endDragging();
715 void ComponentLayout::stretchSelectedComps (int dw, int dh, bool allowSnap)
717 int neww, newh;
719 if (document != 0 && selected.getNumSelected() == 1)
721 Component* const c = selected.getSelectedItem (0);
723 if (allowSnap)
725 int bot = c->getBottom() + dh;
726 int right = c->getRight() + dw;
727 bot = (dh != 0) ? document->snapPosition (bot) : bot;
728 right = (dw != 0) ? document->snapPosition (right) : right;
729 newh = bot - c->getY();
730 neww = right - c->getX();
732 else
734 newh = c->getHeight() + dh;
735 neww = c->getWidth() + dw;
738 c->setSize (neww, newh);
740 updateStoredComponentPosition (c, true);
742 else
744 for (int i = 0; i < selected.getNumSelected(); ++i)
746 Component* const c = selected.getSelectedItem (i);
748 neww = c->getWidth() + dw;
749 newh = c->getHeight() + dh;
750 c->setSize (neww, newh);
752 updateStoredComponentPosition (c, true);
758 //==============================================================================
759 void ComponentLayout::fillInGeneratedCode (GeneratedCode& code) const
761 for (int i = 0; i < components.size(); ++i)
763 Component* const comp = components[i];
764 ComponentTypeHandler* const type = ComponentTypeHandler::getHandlerFor (*comp);
766 if (type != 0)
767 type->fillInGeneratedCode (comp, code);
771 //==============================================================================
772 const String ComponentLayout::getComponentMemberVariableName (Component* comp) const
774 if (comp == 0)
775 return String::empty;
777 String name (comp->getProperties() ["memberName"].toString());
779 if (name.isEmpty())
780 name = getUnusedMemberName (makeValidCppIdentifier (comp->getName(), true, true, false), comp);
782 return name;
785 void ComponentLayout::setComponentMemberVariableName (Component* comp, const String& newName)
787 const String oldName (getComponentMemberVariableName (comp));
789 comp->getProperties().set ("memberName", String::empty);
791 const String n (getUnusedMemberName (makeValidCppIdentifier (newName, false, true, false), comp));
792 comp->getProperties().set ("memberName", n);
794 if (n != oldName)
795 changed();
798 const String ComponentLayout::getUnusedMemberName (String nameRoot, Component* comp) const
800 String n (nameRoot);
802 while (CharacterFunctions::isDigit (nameRoot.getLastCharacter()))
803 nameRoot = nameRoot.dropLastCharacters (1);
805 int suffix = 2;
807 for (;;)
809 bool alreadyUsed = false;
811 for (int i = 0; i < components.size(); ++i)
813 if (components[i] != comp
814 && components[i]->getProperties() ["memberName"] == n)
816 alreadyUsed = true;
817 break;
821 if (! alreadyUsed)
822 break;
824 n = nameRoot + String (suffix++);
827 return n;
830 //==============================================================================
831 const String ComponentLayout::getComponentVirtualClassName (Component* comp) const
833 if (comp == 0)
834 return String::empty;
836 return comp->getProperties() ["virtualName"];
839 void ComponentLayout::setComponentVirtualClassName (Component* comp, const String& newName)
841 const String name (makeValidCppIdentifier (newName, false, false, true));
843 if (name != getComponentVirtualClassName (comp))
845 comp->getProperties().set ("virtualName", name);
846 changed();
850 //==============================================================================
851 void ComponentLayout::addToXml (XmlElement& xml) const
853 for (int i = 0; i < components.size(); ++i)
855 ComponentTypeHandler* h = ComponentTypeHandler::getHandlerFor (*components [i]);
857 if (h != 0)
858 xml.addChildElement (h->createXmlFor (components [i], this));